//===============================
//  功能 ：备注 
//  作者 ：上中野辅亚瑟王 
//  创建时间 ：2023-07-17  19-16-43 
//  Unity版本  ：2019.4.35f1 
//  变更时间 :  2023-07-17  19-16-43 
//===============================





using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

namespace ZS.U3D.BASE.Runtime.Routine 
{
    public static class RoutineManager
    {
        private static bool _isInitialize = false;
        private static GameObject _driver = null;
        public static GameObject Driver { get { return _driver; } }
        private static MonoBehaviour _behaviour;

        //最大允许线程
        private static int maxThreads = 8;
        private static int numThreads;
        private static int mainThreadID;
        private static Queue actions = new Queue();
        private static bool disposed;
        private static WaitForEndOfFrame WaitForEndOfFrame;

        /// <summary>
        /// 初始化
        /// </summary>
        public static void Initialize()
        {
            if (_isInitialize)
            {
                MyDebug.LogWarning($"{nameof(RoutineManager)} is Initialized ");
                return;
            }
            {
                _isInitialize = true;
                _driver = new GameObject($"[{nameof(RoutineManager)}]");
                _behaviour = _driver.AddComponent<RoutineDriver>();
                UnityEngine.Object.DontDestroyOnLoad(_driver);
                disposed = false;
                mainThreadID = Thread.CurrentThread.ManagedThreadId;
                actions = Queue.Synchronized(actions);
                WaitForEndOfFrame = new WaitForEndOfFrame();
                MyDebug.Log($"{nameof(RoutineManager)} is initalize !");
            }
        }

        internal static void Update()
        {
            for (int i = 0; i < actions.Count; i++)
            {
                var action = actions.Dequeue() as Action;
                action?.Invoke();
            }
        }

        /// <summary>
        /// 销毁单例系统
        /// </summary>
        public static void Destroy()
        {
            if (_isInitialize)
            {
                _isInitialize = false;
                if (_driver != null)
                {
                    UnityEngine.Object.Destroy(_driver);
                }
                disposed = true;
                MyDebug.Log($"{nameof(RoutineManager)} destroy !");
            }
        }


        /// <summary>
        /// 开启一个协程
        /// </summary>
        public static Coroutine StartCoroutine(IEnumerator coroutine)
        {
            return _behaviour.StartCoroutine(coroutine);
        }
        public static Coroutine StartCoroutine(string methodName)
        {
            return _behaviour.StartCoroutine(methodName);
        }

        /// <summary>
        /// 停止一个协程
        /// </summary>
        public static void StopCoroutine(Coroutine coroutine)
        {
            _behaviour.StopCoroutine(coroutine);
        }
        public static void StopCoroutine(IEnumerator coroutine)
        {
            _behaviour.StopCoroutine(coroutine);
        }
        public static void StopCoroutine(string methodName)
        {
            _behaviour.StopCoroutine(methodName);
        }

        /// <summary>
        /// 停止所有协程
        /// </summary>
        public static void StopAllCoroutines()
        {
            _behaviour.StopAllCoroutines();
        }


        public static void WaitForFrame(Action action)
        {
            StartCoroutine(WaitForFrameRoutine(action));
        }

        private static IEnumerator WaitForFrameRoutine(Action action)
        {
            yield return WaitForEndOfFrame;
            action?.Invoke();
        }

        /// <summary>
        /// 等待
        /// </summary>
        /// <param name="action">等待回调</param>
        /// <param name="b">等待条件</param>
        public static void WaitFor(Action action, bool b)
        {
            StartCoroutine(WaitForRoutine(action, b));
        }
        private static IEnumerator WaitForRoutine(Action action, bool b)
        {
            while (!b)
            {
                yield return WaitForEndOfFrame;
            }
            action?.Invoke();
        }

        /// <summary>
        /// 等待时间
        /// </summary>
        /// <param name="action">等待中</param>
        /// <param name="time">等待时间</param>
        public static void WaitFor(Action<float> action, float time, GameObject gameObject)
        {
            StartCoroutine(WaitForRoutine(null, action, null, time, gameObject == null));
        }

        /// <summary>
        /// 等待结束
        /// </summary>
        /// <param name="end">等待结束事件</param>
        /// <param name="time">等待时间</param>
        public static void WaitFor(Action end, float time, GameObject gameObject)
        {
            StartCoroutine(WaitForRoutine(null, null, end, time, gameObject == null));
        }

        /// <summary>
        /// 等待结束
        /// </summary>
        /// <param name="end">等待结束事件</param>
        /// <param name="time">等待时间</param>
        public static void WaitFor(Action end, float time)
        {
            WaitFor(end, time, Driver);
        }

        /// <summary>
        /// 等待结束
        /// </summary>
        /// <param name="end">等待结束事件</param>
        /// <param name="time">等待时间</param>
        public static void WaitFor(Action start, Action end, float time, GameObject gameObject)
        {
            StartCoroutine(WaitForRoutine(start, null, end, time, gameObject == null));
        }


        /// <summary>
        /// 等待时间
        /// </summary>
        /// <param name="start"> 等待开始 </param>
        /// <param name="action"> 等待中 </param>
        /// <param name="end">等待结束</param>
        /// <param name="time">等待时间</param>
        public static void WaitFor(Action start, Action<float> action, Action end, float time, GameObject gameObject)
        {
            StartCoroutine(WaitForRoutine(start, action, end, time, gameObject == null));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="start"></param>
        /// <param name="action"></param>
        /// <param name="end"></param>
        /// <param name="time"></param>
        /// <param name="b"> 中断条件 </param>
        /// <returns></returns>
        private static IEnumerator WaitForRoutine(Action start, Action<float> action, Action end, float time, bool b)
        {
            float currenttime = 0;
            start?.Invoke();
            currenttime += Time.deltaTime;
            while (currenttime < time)
            {
                currenttime += Time.deltaTime;
                yield return WaitForEndOfFrame;
                if (b)
                {
                    MyDebug.LogWarning($"满足条件 {b} ,协程终止");
                    yield break;
                }
                action?.Invoke(currenttime);
            }
            if (b)
            {
                MyDebug.LogWarning($"满足条件 {b} ,协程终止");
                yield break;
            }
            action?.Invoke(time);
            end?.Invoke();
        }

        public static bool IsInMainThread()
        {
            if (disposed)
                return false;
            return Thread.CurrentThread.ManagedThreadId == mainThreadID;
        }

        /// <summary>
        /// 缓冲至主线程执行
        /// </summary>
        /// <param name="action"></param>
        public static void QueueInMainThread(Action action)
        {
            if (disposed) return;
            actions.Enqueue(action);
        }

        /// <summary>
        /// 异步执行
        /// </summary>
        /// <param name="action"></param>
        /// <returns></returns>
        public static Thread RunAsync(Action action)
        {
            if (disposed) return null;
            while (numThreads >= maxThreads)
            {
                Thread.Sleep(1);
            }
            Interlocked.Increment(ref numThreads);
            ThreadPool.QueueUserWorkItem((obj) =>
            {
                try
                {
                    ((Action)obj)();
                }
                catch { }
                finally
                {
                    Interlocked.Decrement(ref numThreads);
                }
            }, action);
            return null;
        }
    }
}

